package org.rx;

import java.io.FileOutputStream;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Collection;

public class SXPNumbering {
	static int high;
	static final int bits = 8;
	
	static {
		high = bits - Hierarchy.nbBits(Hierarchy.concretes.size()+1);		
		Hierarchy.hierarchy.numbering(0, 0);
		
		int i = 1;
		for(Concrete node: Hierarchy.concretes) // This is not done via numbering to ensure a total order on leaves
			node.id =	(i++ << high)
					|	(Hierarchy.nbOnes(high) ^ Hierarchy.nbOnes(node.parent.mask))
					|	(node.parent.id & Hierarchy.nbOnes(node.parent.mask)); 

//		System.err.println(Hierarchy.abstracts);
//		System.err.println(Hierarchy.concretes);
		
		/////////////////// Sanity Checks \\\\\\\\\\\\\\\\\\
		int max = 0;
		for(Abstract node: Hierarchy.abstracts)
			max = Math.max(node.mask, max);
		if((high+max) > bits)
			throw new RuntimeException("Number of bits exceded: " + Hierarchy.concretes.size()+ " ("+ (bits-high)  + ") "+ Hierarchy.abstracts.size() +" ("+ max+")");
		for(Concrete node: Hierarchy.concretes){ // Only on leaves since nodes have no instances
//			System.out.println("Checking "+node+" ====> "+nbBits(node.id));
			node.checkIntegrity();
		}
		/////////////////// Sanity Checks \\\\\\\\\\\\\\\\\\
	}
	public static void main(String[] args) throws Exception{
		PrintStream out = System.out;
		if(args.length > 0)
			out = new PrintStream(new FileOutputStream(args[0]));
		//out = new PrintStream(new FileOutputStream("/Users/morandat/work/src/reactor/src/sxp_int.h"));
		generate_ids(out);
	}
	
	static void generate_header(PrintStream out){
		out.println("#ifndef _RTYPES_H_");
		out.println("#define _RTYPES_H_\n");
		out.println("#define SXP_LOW_PART\t"+high+"\n");
	}
	
	static void generate_footer(PrintStream out){
		out.println("#endif /* RTYPES_H_ */");
	}	
	
	static void generate_ids(PrintStream out){
		generate_header(out);
		Hierarchy.hierarchy.generate_id(out);
		generate_footer(out);
	}
	
	static void generate_macros(PrintStream out){
		generate_header(out);
		out.println("#define CALL(__type, __method, ...)\\");
		out.println("\tCALL_##__type(__method, __VA_ARGS__)");
		out.println("#define DEFINE_METHOD(__method, ...)\\");
		out.println("\tDEFINE_METHOD_CAT_TOKENS(DEFINE_METHOD_, __VA_ARGS__) (__method, __VA_ARGS__)");
		out.println("#define EXPORT_METHOD(__method, ...)\\");
		out.println("\tDEFINE_METHOD_CAT_TOKENS(EXPORT_METHOD_, __VA_ARGS__) (__method, __VA_ARGS__)");
		out.println();
		out.println("#define DEFINE_METHOD_MAKE_SIG(__return_type, ...)\\");
		out.println("\t(__return_type (*) (Star##__VA_ARGS__))");
		out.println("#define DEFINE_METHOD_MAKE_DECL(__method, __return_type, ...)\\");
		out.println("\t__return_type (*__method[]) (Star##__VA_ARGS__)");
		out.println("#define DEFINE_METHOD_CAT_TOKENS(__token, __ret, __type, ...)\\");
		out.println("\t__token##__type");
		out.println();
		out.println("#define CALL_METHOD_RELAY_CALL(__x, ...)\\\n\t((TYPE(__x) >> SXP_LOW_PART) - 1)");
		Hierarchy.hierarchy.generate_macro(out);
		//number.generate_macro(out);
	}
	
	static abstract class Node {
		int id;
		String name;
		Abstract parent;
		int mask;

		Node(String name, Abstract parent){
			this.name = name;
			this.parent = parent;
			if(parent != null)
				parent.add(this);
		}
		
		int numbering(int current, int mask) { return 0; }
		abstract int getLeaves(ArrayList<Concrete> leaves);
		
		boolean is_supertype_of(int i){
			int sign = Hierarchy.nbOnes(mask); 
			return ((i & sign) ^ (id & sign)) == 0;
		}

		abstract Node supertype_of_all(Collection<Node> concretes);

		void generate_id(PrintStream out_header){
			String n = name;
			out_header.printf("#define %s_ID%s\t%d\n", n, n.length()>4? "" : "\t", id);
			out_header.printf("#define %s_MASK\t%d\n", n, Hierarchy.nbOnes(mask));
		}
		
		void generate_macro(PrintStream out_header){
			String n = name;
			generate_id(out_header);
			out_header.printf("#define %s\\\n\t(!((__x & %s_MASK) ^ (%s_ID & %s_MASK)))\n", isA("__x"), n, n, n);
			out_header.printf("#define Star%s %s*\n", n, n);
		}
		
		String isA(String x){
			return "IS_"+name+"("+x+")";
		}
		
		void generate_vt_dipatch(PrintStream out_header, ArrayList<Concrete> leaves){
//			out.println("#define DEFINE_METHOD_int(__method, ...)\\");
//			out.println("\tDEFINE_METHOD_MAKE_DECL( __method, __VA_ARGS__) = { DEFINE_METHOD_MAKE_SIG(__VA_ARGS__) DEFINE_METHOD_CAT_TOKENS(__method ## _, __VA_ARGS__) }");

			out_header.printf("#define DEFINE_METHOD_%s(__method, ...)\\\n", name);
			int nbleaves = leaves.size();
			out_header.print("\tDEFINE_METHOD_MAKE_DECL(__method, __VA_ARGS__) = {");
			for(int i = 0; i < nbleaves; i++){
				if(i % 8 == 0)
					out_header.print("\\\n\t\t");
				out_header.printf("DEFINE_METHOD_MAKE_SIG(__VA_ARGS__) __method##_%s", leaves.get(i).name);
				if(i == nbleaves - 1)
					out_header.println("\\\n\t}");
				else
					out_header.print(", ");
			}
			out_header.printf("#define CALL_%s(__method, ...)\\\n\t__method[CALL_METHOD_RELAY_CALL(__VA_ARGS__,)](__VA_ARGS__)\n", name);
			out_header.printf("#define EXPORT_METHOD_%s(__method, ...)\\\n", name);
			out_header.println("\textern DEFINE_METHOD_MAKE_DECL(__method, __VA_ARGS__)");
		}
		
		void generate_bt_dispatch(PrintStream out_header, ArrayList<Concrete> leaves){
			out_header.printf("#define DEFINE_METHOD_%s(__name)\\\n", name);
			out_header.println("do{\\");
			generate_branchs(out_header, leaves, 0, 0, leaves.size() - 1);
			out_header.println("}while(0)");
		}
		
		private void generate_branchs(PrintStream out_header, ArrayList<Concrete> leaves, int level, int start, int stop){
			if((stop - start)  < 1)
				out_header.printf("__f%d(recv, ...) // %s\\\n", start, leaves.get(start).name);
			else {
				int mid = (stop - start) / 2 + start;
				out_header.printf("%."+level+"sif(__recv < %s_ID){\\\n", "", leaves.get(mid+1).name);
				generate_branchs(out_header, leaves, level+1, start, mid);
				out_header.printf("%."+level+"s}else{\\\n","");
				generate_branchs(out_header, leaves, level+1, mid+1, stop);
				out_header.printf("%."+level+"s}\\\n","");
			}
		}
		
		public String toString(){
			return String.format("%s:%2d:%2d\t%8s:%8s", name, id, mask, Integer.toBinaryString(id), Integer.toBinaryString(mask));
		}
		
		void test(Node i){
			System.out.println(i.name+":"+i.id+" is a "+name+":"+id+":"+mask+" "+is_supertype_of(i.id));
		}
		void checkIntegrity(){
			for(Node node: Hierarchy.abstracts){
				boolean is_super = node.is_supertype_of(id);
				boolean is_parent = false;
				Node c = this;
				while(c != null){
					if(is_parent = c == node)
						break;
					c = c.parent;
				}
				if(is_parent != is_super)
					throw new RuntimeException("Not verified "+this+" "+node+"\t"+is_parent+" "+is_super);
			}
		}
	}
	
	static class Concrete extends Node{
		Concrete(String name, Abstract parent){
			super(name, parent);
			Hierarchy.concretes.add(this);
			mask = bits;
		}
		int getLeaves(ArrayList<Concrete> leaves){
			if(leaves != null)
				leaves.add(this);
			return 1;
		}
		void generate_macro(PrintStream out_header){
			super.generate_macro(out_header);
			out_header.printf("#define CALL_%s(__method, ...)\\\n\t__method##_%s](__VA_ARGS__)\n", name, name);
			//out_header.printf("#define EXPORT_METHOD_%s(__method, ...)\\\n", name);
			//out_header.println("\textern DEFINE_METHOD_MAKE_DECL(__method, __VA_ARGS__)");
		}
		Node supertype_of_all(Collection<Node> concretes){
			for(Node n: concretes)
				if(n != this)
					return null;
			return this;
		}
	}
	static class Root extends Abstract{
		Root(String name, Abstract parent){
			super(name, parent);
		}
	}	
	static class Abstract extends Node{
		ArrayList<Node> children;
		int nbabstracts;
		Abstract(String name, Abstract parent){
			super(name, parent);
			Hierarchy.abstracts.add(this);
			children = new ArrayList<Node>();
			if(parent != null)
				parent.nbabstracts ++;
		}
		void add(Node node){
			children.add(node);
		}
		int getLeaves(ArrayList<Concrete> leaves){
			int i = 0;
			for(Node child: children)
				i += child.getLeaves(leaves);
			return i;		
		}
		int numbering(int current, int msize) {
			id = (Hierarchy.nbOnes(bits) ^ Hierarchy.nbOnes(high)) | current;
			mask = msize;
			int i = 0;
			int nmsize = Hierarchy.nbBits(nbabstracts) + msize;
			for(Node child: children){
				if(child.numbering(i << msize | current, nmsize) > 0)
					i ++;
			}
			return 1;
		}
		Node supertype_of_all(Collection<Node> concretes){
			for(Node cls: concretes)
				if(!is_supertype_of(cls.id))
					return null;
			for(Node chld: children){
				Node candidate = chld.supertype_of_all(concretes);
				if(candidate != null)
					return candidate;
			}
			return this;
		}
		void test(Node i){
			super.test(i);
			for(Node child: children)
				child.test(i);
		}
		void generate_id(PrintStream out_header){
			super.generate_id(out_header);
			for(Node child: children)
				child.generate_id(out_header);
		}
		void generate_macro(PrintStream out_header){
			super.generate_macro(out_header);
			ArrayList<Concrete> leaves = new ArrayList<Concrete>();

			if(getLeaves(leaves) >= 0)
				generate_vt_dipatch(out_header, leaves);
			else
				generate_bt_dispatch(out_header, leaves);
			
			for(Node child: children)
				child.generate_macro(out_header);		
		}
	}
}